package com.zq.assistant.func.ioc.injector;

import android.content.Context;
import android.view.View;

import com.zq.assistant.func.ioc.annotations.InjectView;
import com.zq.assistant.func.ioc.finder.FinderFactory;
import com.zq.assistant.func.ioc.finder.FinderFactoryIMPL;
import com.zq.assistant.func.ioc.annotations.InjectListener;
import com.zq.assistant.func.ioc.listener.Listener;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

/**
 * Created by zhangqiang on 17-7-7.
 */

public class InjectorImpl implements Injector {

    private FinderFactory finderFactory;

    public InjectorImpl() {
        this(new FinderFactoryIMPL());
    }

    public InjectorImpl(FinderFactory finderFactory) {
        this.finderFactory = finderFactory;
    }

    @Override
    public void inject(Context context, final Object target) {

        Class<?> targetClass = target.getClass();
        List<Field> fieldList = finderFactory.getFieldFinder(target).findInjectFields(targetClass,InjectView.class);
        for (Field field:
             fieldList) {

            field.setAccessible(true);
            InjectView injectView = field.getAnnotation(InjectView.class);
            int viewId = injectView.id();

            if(viewId < 0){
                continue;
            }
            View view = finderFactory.getViewFinder(target).findViewById(viewId);
            try {
                field.set(target,view);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }


        List<Method> methodList = finderFactory.getMethodFinder(target).findInjectMethod(targetClass,InjectListener.class);
        for (Method method:
                methodList ) {

            InjectListener injectListener = method.getAnnotation(InjectListener.class);

            int[] viewIds = injectListener.viewIds();
            Listener listener = injectListener.listener();

            for (int viewId:
                    viewIds) {

                final View view = finderFactory.getViewFinder(target).findViewById(viewId);

                String setterMethodName = listener.getSetterMethodName();
                Class listenerClass = listener.getListenerInterface();

                try {

                    Method setterMethod = findSetterMethod(view.getClass(),setterMethodName,listenerClass);

                    if(setterMethod == null){
                        continue;
                    }

                    Object listenerProxy = Proxy.newProxyInstance(targetClass.getClassLoader(),
                            new Class[]{listenerClass},
                            new DefaultInvocationHandler(target,method));

                    setterMethod.invoke(view,listenerProxy);
                }catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static class DefaultInvocationHandler implements InvocationHandler{

        private Object target;
        private Method proxyMethod;

        DefaultInvocationHandler(Object target, Method proxyMethod) {
            this.target = target;
            this.proxyMethod = proxyMethod;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return proxyMethod.invoke(target,args);
        }
    }

    private Method findSetterMethod(Class<?> viewClass,String methodName,Class...args){

        while(View.class.isAssignableFrom(viewClass)){

            try {
                return  viewClass.getMethod(methodName,args);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
                viewClass = viewClass.getSuperclass();
            }
        }
        return null;
    }
}
